/**
 * 
 */
package neptune.web.client.view.components;

import java.util.Date;
import neptune.web.client.WebInterface;
import neptune.web.client.controller.AddUserToExperimentController;
import neptune.web.client.controller.RemoveUserFromExperimentController;
import neptune.web.client.model.DefaultRoles;
import neptune.web.client.model.SerializableExperiment;
import neptune.web.client.model.SerializableExperimentState;
import neptune.web.client.services.ApplicationDataProvider;
import neptune.web.client.services.experiment.ExperimentService;
import neptune.web.client.view.components.UserTable.UserTableListener;
import neptune.web.client.view.components.dialogs.LinKernighanAllocatorDialog;
import neptune.web.client.view.components.dialogs.ManualAllocationDialog;
import neptune.web.client.view.components.dialogs.PropertiesDialog;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;

/**
 * @author Roberto Bifulco [info@RobertoBifulco.it] [RobertoBifulco.it]
 * @author Luigi Lorenzo Arpino [luigiarpino@yahoo.it]
 * 
 */
public class ExperimentAdministrationPanel extends Composite {

	private final String undefined = "img/undefined.png";
	private final String undefLabel = "The experiment is created but no valid "
			+ "topology is associated with it.";
	private final String defined = "img/defined.png";
	private final String defLabel = "The experiment's topology is defined and valid. "
			+ "The experiment can be allocated.";
	private final String waiting = "img/waiting.png";
	/*
	 * private final String waitingLabel = "An allocation request for " +
	 * "the experiment was sent. To cancel the request just use" +
	 * " the \"SWAP OUT\" button.";
	 */
	private final String waitingLabel = "An allocation request for "
			+ "the experiment was sent. Please wait!";
	private final String allocated = "img/allocated.png";
	private final String allocLabel = "Experiment's node are allocated on the cluster "
			+ "but no topology is enforced on them. "
			+ "Topology configuration is needed.";
	private final String running = "img/running.png";
	private final String runningLabel = "The experiment is fully "
			+ "operative on the cluster.";

	private Label experimentName;
	private Label admin;
	private Label creationTime;
	private Label state;
	private Label startTime;
	private Label stopTime;
	private Label expSubnet;

	Button buttonoRequestAllocation;
	Button buttonTerminate;
	Button buttonSwapOut;
	Button buttonDeploy;
	Button buttonEnforceTopology;

	private ClickListener requestAllocationButtonListener = new ClickListener() {
		public void onClick(Widget sender) {
			if (!Window
					.confirm("This operation sends an allocation request for the experiment '"
							+ experimentName.getText() + "'. Are you sure?"))
				return;

			final PopupPanel popup = WebInterface.showMessageDialog(
					new MessageWidget(MessageWidget.LOADING,
							"Sending allocation request..."), false);

			ExperimentService.Util.getInstance().allocationRequest(
					experimentName.getText(), new AsyncCallback<String>() {
						public void onSuccess(String result) {
							popup.hide();
							setExperimentState(result);
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' allocation request sent.";
							WebInterface.addMessageToConsoleAndShowDialog(
									message, false);
						}

						public void onFailure(Throwable caught) {
							popup.hide();
							String message = "Impossible to send experiment '"
									+ experimentName.getText()
									+ "' allocation request. Cause:\n"
									+ caught.getMessage();
							caught.printStackTrace();
							WebInterface.addMessageToConsoleAndShowDialog(
									message, true);
						}
					});
		}
	};
	private ClickListener terminateButtonListener = new ClickListener() {

		public void onClick(Widget sender) {
			if (!Window
					.confirm("Warning: a terminated experiment can't be allocated again.\nTerminate experiment '"
							+ experimentName.getText() + "'?"))
				return;

			final PopupPanel popup = WebInterface.showMessageDialog(
					new MessageWidget(MessageWidget.LOADING,
							"Terminating experiment..."), false);

			ExperimentService.Util.getInstance().terminateExperiment(
					experimentName.getText(), new AsyncCallback<String>() {
						public void onSuccess(String result) {
							popup.hide();
							setExperimentState(result);
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' terminated.";
							WebInterface.addMessageToConsoleAndShowDialog(
									message, false);
						}

						public void onFailure(Throwable caught) {
							popup.hide();
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' termination failed. Cause:\n"
									+ caught.getMessage();
							WebInterface.addMessageToConsoleAndShowDialog(
									message, true);
						}
					});
		}
	};
	private ClickListener swapOutListener = new ClickListener() {
		public void onClick(Widget sender) {
			if (!Window.confirm("SwapOut experiment '"
					+ experimentName.getText() + "'?"))
				return;

			final PopupPanel popup = WebInterface.showMessageDialog(
					new MessageWidget(MessageWidget.LOADING,
							"Stopping experiment..."), false);

			ExperimentService.Util.getInstance().deallocateExperiment(
					experimentName.getText(), new AsyncCallback<String>() {
						public void onSuccess(String result) {
							popup.hide();
							setExperimentState(result);
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' deallocated.";
							WebInterface.addMessageToConsoleAndShowDialog(
									message, false);
						}

						public void onFailure(Throwable caught) {
							popup.hide();
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' deallocation failed. Cause:\n"
									+ caught.getMessage();
							WebInterface.addMessageToConsoleAndShowDialog(
									message, true);
						}
					});
		}
	};
	// l'utente vuole effettuare il Deploy
	private ClickListener allocateButtonListener = new ClickListener() {
		public void onClick(Widget sender) {
			boolean auto = Window
					.confirm("Do you want to use Lin-Kernighan algorithm experiment allocation?");
			if (auto) {
				/*
				 * Command deployCommand = new DeployExperimentCommand(
				 * experimentName.getText() ); deployCommand.execute();
				 */
				LinKernighanAllocatorDialog dialog = new LinKernighanAllocatorDialog(
						experimentName.getText());
				dialog.center();
			} else {
				(new ManualAllocationDialog(experimentName.getText())).center();
			}
		}
	};
	private ClickListener forceSetupListener = new ClickListener() {
		public void onClick(Widget sender) {
			if (!Window.confirm("Force configuration for experiment '"
					+ experimentName.getText() + "'?"))
				return;

			final PopupPanel popup = WebInterface.showMessageDialog(
					new MessageWidget(MessageWidget.LOADING,
							"Configuring experiment..."), false);

			ExperimentService.Util.getInstance().forceExperimentConfiguration(
					experimentName.getText(), new AsyncCallback<String>() {
						public void onSuccess(String result) {
							popup.hide();
							setExperimentState(result);
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' correctly configured.";
							WebInterface.addMessageToConsoleAndShowDialog(
									message, false);
						}

						public void onFailure(Throwable caught) {
							popup.hide();
							String message = "Experiment '"
									+ experimentName.getText()
									+ "' configuration failed. Cause:\n"
									+ caught.getMessage();
							WebInterface.addMessageToConsoleAndShowDialog(
									message, true);
						}
					});
		}
	};
	private Panel adminCommandsPanel;
	private UserTable userTable;
	private UserTableListener userTableListener;
	private Image actualStatusImg;
	private Label statusLabel;
	private Label descrLabel;

	public ExperimentAdministrationPanel() {
		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setWidth("100%");

		// Nome dell'esperimento
		HorizontalPanel headerPanel = new HorizontalPanel();
		headerPanel.setWidth("100%");
		headerPanel.setVerticalAlignment(HorizontalPanel.ALIGN_BOTTOM);
		experimentName = new Label();
		headerPanel.add(experimentName);
		experimentName.addStyleName("ExperimentView-nameValue");

		// Commands
		HorizontalPanel commandsPanelWrap = new HorizontalPanel();
		commandsPanelWrap.add(buildCommandsPanel());
		boolean isSystemAdmin = true/*
									 * ApplicationDataProvider.getInstance()
									 * .getActualUserRole
									 * ().equals(DefaultRoles.SYSTEM_ADMIN)
									 */;
		if (isSystemAdmin) {
			adminCommandsPanel = buildAdminCommandsPanel();
			commandsPanelWrap.add(adminCommandsPanel);
		}
		headerPanel.add(commandsPanelWrap);
		headerPanel.setCellHorizontalAlignment(commandsPanelWrap,
				HorizontalPanel.ALIGN_RIGHT);
		vPanel.add(headerPanel);

		// Admin dell'esperimento
		HorizontalPanel adminPanel = new HorizontalPanel();
		adminPanel.setVerticalAlignment(HorizontalPanel.ALIGN_BOTTOM);
		Label adminLabel = new Label("Experiment Admin:");
		admin = new Label();
		adminPanel.add(adminLabel);
		adminPanel.add(admin);

		adminLabel.addStyleName("ExperimentView-adminLabel");
		admin.addStyleName("ExperimentView-adminValue");
		vPanel.add(adminPanel);

		// Status
		vPanel.add(buildExperimentStatus());

		// help
		HTML help = new HTML("In this tab a summary of the experiment "
				+ "is presented."
				+ " From here you can manage the experiment's users, "
				+ "the start/stop of the experiment and "
				+ "view experiment's state informations.");
		vPanel.add(help);

		// Datas
		HorizontalPanel datasPanel = new HorizontalPanel();
		vPanel.add(datasPanel);

		// User Panel
		datasPanel.add(buildUsersPanel());
		// Exp infos
		datasPanel.add(buildExpDataPanel());

		// Styles
		help.setStylePrimaryName("ExperimentView-summaryHelp");

		initWidget(vPanel);
	}

	public void setData(SerializableExperiment exp) {
		boolean isSystemAdmin = ApplicationDataProvider.getInstance()
				.getActualUserRole().equals(DefaultRoles.SYSTEM_ADMIN);

		if (isSystemAdmin) {
			if (adminCommandsPanel != null)
				adminCommandsPanel.setVisible(true);
			else
				adminCommandsPanel = buildAdminCommandsPanel();
		} else if (adminCommandsPanel != null)
			adminCommandsPanel.setVisible(false);

		DateTimeFormat dateFormat = DateTimeFormat.getFormat("M.d.y k:m:s v");

		experimentName.setText(exp.getExperimentId());
		admin.setText(exp.getAdministrator());
		creationTime.setText(dateFormat.format(new Date(exp.getState()
				.getCreationTimestamp())));
		startTime.setText(dateFormat.format(new Date(exp.getState()
				.getStartTimestamp())));
		stopTime.setText(dateFormat.format(new Date(exp.getState()
				.getStopTimestamp())));

		state.setText(exp.getState().getState());

		expSubnet.setText(exp.getAssignedSubnet().getAddress() + "/"
				+ exp.getAssignedSubnet().getSubnetMask());

		for (String username : exp.getUsers())
			userTable.addUsername(username);

		// Aggiornamento status icon
		setStatusIcon(exp.getState().getState());

		// Aggiornamento dei componenti dinamici della lista utenti
		updateUserTableListener();
	}

	public void reset() {
		admin.setText("");
		creationTime.setText("");
		experimentName.setText("");
		startTime.setText("");
		state.setText("");
		stopTime.setText("");
		expSubnet.setText("");
		statusLabel.setText("-");
		descrLabel.setText("");
		actualStatusImg.setUrl("");
		userTable.removeAllUsernames();
	}

	public void setExperimentState(String state) {
		this.state.setText(state);
		setStatusIcon(state);
	}

	public UserTable getUserTable() {
		return userTable;
	}

	private Panel buildCommandsPanel() {
		HorizontalPanel hPanel = new HorizontalPanel();

		buttonoRequestAllocation = new Button("Request Allocation");
		buttonoRequestAllocation.setVisible(false);
		buttonoRequestAllocation
				.setStylePrimaryName("ExperimentView-commandButton");
		buttonoRequestAllocation
				.addClickListener(requestAllocationButtonListener);

		buttonTerminate = new Button("Terminate");
		buttonTerminate.setVisible(false);
		buttonTerminate.setStylePrimaryName("ExperimentView-commandButton");
		buttonTerminate.addClickListener(terminateButtonListener);

		buttonSwapOut = new Button("Swap out");
		buttonSwapOut.setVisible(false);
		buttonSwapOut.setStylePrimaryName("ExperimentView-commandButton");
		buttonSwapOut.addClickListener(swapOutListener);

		hPanel.add(buttonoRequestAllocation);
		hPanel.add(buttonSwapOut);
		hPanel.add(buttonTerminate);

		hPanel.setStylePrimaryName("ExperimentView-commandPanel");

		return hPanel;
	}

	private Panel buildAdminCommandsPanel() {

		buttonDeploy = new Button("Deploy");
		buttonDeploy.setVisible(false);
		buttonDeploy.setStylePrimaryName("ExperimentView-adminCommandButton");
		buttonDeploy.addClickListener(allocateButtonListener);

		buttonEnforceTopology = new Button("Enforce Topology");
		buttonEnforceTopology.setVisible(false);
		buttonEnforceTopology
				.setStylePrimaryName("ExperimentView-adminCommandButton");
		buttonEnforceTopology.addClickListener(forceSetupListener);

		HorizontalPanel adminPanel = new HorizontalPanel();
		adminPanel.add(buttonDeploy);
		adminPanel.add(buttonEnforceTopology);
		adminPanel.setStylePrimaryName("ExperimentView-adminCommandPanel");

		return adminPanel;
	}

	private DisclosurePanel buildUsersPanel() {

		DisclosurePanel panel = new DisclosurePanel("Experiment users");
		panel.setAnimationEnabled(true);

		userTable = new UserTable("Users", userTableListener, 150);
		panel.add(userTable);

		panel.addStyleName("ExperimentView-userPanel");

		return panel;
	}

	private DisclosurePanel buildExpDataPanel() {
		DisclosurePanel panel = new DisclosurePanel("Experiment infos");
		panel.setAnimationEnabled(true);

		FlexTable table = new FlexTable();
		HorizontalPanel hPanel = new HorizontalPanel();
		panel.add(hPanel);
		hPanel.add(table);

		// Timestamp creazione
		Label creationTimeLabel = new Label("Creation timestamp:");
		creationTime = new Label();
		table.setWidget(0, 0, creationTimeLabel);
		table.setWidget(0, 1, creationTime);

		creationTimeLabel.addStyleName("ExperimentView-dataLabel");
		creationTime.addStyleName("ExperimentView-dataValue");

		// Stato esperimento
		Label stateLabel = new Label("Experiment state:");
		state = new Label();
		table.setWidget(1, 0, stateLabel);
		table.setWidget(1, 1, state);

		stateLabel.addStyleName("ExperimentView-dataLabel");
		state.addStyleName("ExperimentView-dataValue");

		// Timestamp avvio
		Label startTimeLabel = new Label("Start timestamp:");
		startTime = new Label();
		table.setWidget(2, 0, startTimeLabel);
		table.setWidget(2, 1, startTime);

		startTimeLabel.addStyleName("ExperimentView-dataLabel");
		startTime.addStyleName("ExperimentView-dataValue");

		// Timestamp stop
		Label stopTimeLabel = new Label("Stop timestamp:");
		stopTime = new Label();
		table.setWidget(3, 0, stopTimeLabel);
		table.setWidget(3, 1, stopTime);

		stopTimeLabel.addStyleName("ExperimentView-dataLabel");
		stopTime.addStyleName("ExperimentView-dataValue");

		// Subnet
		Label expSubnetLabel = new Label("Experiment Subnet:");
		expSubnet = new Label();
		table.setWidget(4, 0, expSubnetLabel);
		table.setWidget(4, 1, expSubnet);

		expSubnetLabel.addStyleName("ExperimentView-dataLabel");
		expSubnet.addStyleName("ExperimentView-dataValue");

		table.addStyleName("ExperimentView-dataTable");
		panel.addStyleName("ExperimentView-userPanel");

		return panel;
	}

	private Panel buildExperimentStatus() {
		HorizontalPanel panel = new HorizontalPanel();

		VerticalPanel iconPanel = new VerticalPanel();
		iconPanel.setWidth("150px");
		iconPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
		actualStatusImg = new Image(undefined);
		iconPanel.add(actualStatusImg);
		statusLabel = new Label(SerializableExperimentState.UNDEFINED);
		iconPanel.add(statusLabel);

		panel.add(iconPanel);

		VerticalPanel descriptionPanel = new VerticalPanel();
		Label descrHeaderLabel = new Label("Status description:");
		descrLabel = new Label(undefLabel);
		descriptionPanel.add(descrHeaderLabel);
		descriptionPanel.add(descrLabel);

		panel.add(descriptionPanel);

		// Styles
		panel.setStylePrimaryName("ExperimentView-StatusPanel");
		iconPanel.setStylePrimaryName("ExperimentView-StatusIconPanel");
		statusLabel.setStylePrimaryName("ExperimentView-StatusLabel");
		actualStatusImg.setStylePrimaryName("ExperimentView-StatusImg");

		descriptionPanel
				.setStylePrimaryName("ExperimentView-StatusDescriptionPanel");
		descrHeaderLabel
				.setStylePrimaryName("ExperimentView-StatusDescrHeader");
		descrLabel.setStylePrimaryName("ExperimentView-StatusDescr");

		return panel;
	}

	private void setStatusIcon(String status) {
		statusLabel.setText(status);
		if (status.equals(SerializableExperimentState.UNDEFINED)) {
			descrLabel.setText(undefLabel);
			actualStatusImg.setUrl(undefined);
			// setto la visibilità dei bottoni
			this.buttonoRequestAllocation.setVisible(false);
			this.buttonSwapOut.setVisible(false);
			this.buttonTerminate.setVisible(false);
			this.buttonDeploy.setVisible(false);
			this.buttonEnforceTopology.setVisible(false);
		} else if (status.equals(SerializableExperimentState.DEFINED)) {
			descrLabel.setText(defLabel);
			actualStatusImg.setUrl(defined);
			// setto la visibilità dei bottoni
			this.buttonoRequestAllocation.setVisible(true);
			this.buttonSwapOut.setVisible(false);
			this.buttonTerminate.setVisible(false);
			this.buttonDeploy.setVisible(false);
			this.buttonEnforceTopology.setVisible(false);
		} else if (status.equals(SerializableExperimentState.WAITING)) {
			descrLabel.setText(waitingLabel);
			actualStatusImg.setUrl(waiting);
			// setto la visibilità dei bottoni
			this.buttonoRequestAllocation.setVisible(false);
			this.buttonSwapOut.setVisible(false);
			this.buttonTerminate.setVisible(false);
			this.buttonDeploy.setVisible(true);
			this.buttonEnforceTopology.setVisible(false);
		} else if (status.equals(SerializableExperimentState.ALLOCATED)) {
			descrLabel.setText(allocLabel);
			actualStatusImg.setUrl(allocated);
			// setto la visibilità dei bottoni
			this.buttonoRequestAllocation.setVisible(false);
			this.buttonSwapOut.setVisible(false);
			this.buttonTerminate.setVisible(true);
			this.buttonDeploy.setVisible(false);
			this.buttonEnforceTopology.setVisible(true);
		} else if (status.equals(SerializableExperimentState.RUNNING)) {
			descrLabel.setText(runningLabel);
			actualStatusImg.setUrl(running);
			// setto la visibilità dei bottoni
			this.buttonoRequestAllocation.setVisible(false);
			this.buttonSwapOut.setVisible(true);
			this.buttonTerminate.setVisible(true);
			this.buttonDeploy.setVisible(false);
			this.buttonEnforceTopology.setVisible(true);
		}
	}

	private void updateUserTableListener() {
		userTableListener = new UserTable.UserTableListener() {

			public void removeUserPressed(UserTable sender) {
				PropertiesDialog dialog = new PropertiesDialog(
						new String[] { "Username" });
				if (sender.equals(userTable)) {
					dialog.setText("Remove User From Experiment");
					dialog.setListener(new RemoveUserFromExperimentController(
							experimentName.getText()));
				} else {
					dialog.setText("Remove Super User From Experiment");
					dialog.setListener(new RemoveUserFromExperimentController(
							experimentName.getText()));
				}
				dialog.center();
			}

			public void addUserPressed(UserTable sender) {
				PropertiesDialog dialog = new PropertiesDialog(
						new String[] { "Username" });
				if (sender.equals(userTable)) {
					dialog.setText("Add User To Experiment");
					dialog.setListener(new AddUserToExperimentController(false,
							experimentName.getText()));
				} else {
					dialog.setText("Add Super User To Experiment");
					dialog.setListener(new AddUserToExperimentController(true,
							experimentName.getText()));
				}
				dialog.center();
			}

		};

		userTable.setListener(userTableListener);
	}
}
